#pragma once
#pragma warning (disable : 4251 )


#include "util/Singleton.h"
#include "Types.h"
#include <list>
#include <map>
#include "event/IMessengerEventData.h"
#include "util/Shared_ptr.h"


namespace Boon
{
	class ProcessManager;

/*!
* Messenger providing possibility so send Event-Objects to registered
* listeners. Events can be send directly, queued or delayed by time
*/
class BOON_API Messenger : public Singleton<Messenger>
{
	SINGLETON( Messenger )

	#pragma region Type Definition
	typedef std::list<EventListener>			ListListeners; 		/*!< List saving all registered EventListeners */
	typedef std::map<EventType, ListListeners>	MapListenersPerEvent; 		/*!< Saves all registered listeners per event */
	typedef std::list<std::pair<float,IMessengerEventStrong>> ListQueuedEvents; 		/*!< List containing a Pair defining a delay / Event to bang */
	#pragma endregion

public:

	/*! \brief		Registers a event of type id to the given listener
		\param		id - ID fetched by IMessengerEvent::GetEventID()
		\param		listener - Listener pointing to a function or member-method
		\return		bool - True on success
	*/
	bool					Register( const EventType& id, const EventListener& listener );

	/*! \brief		Removes a listener from a given event
		\param		id - ID fetched by IMessengerEvent::GetEventID()
		\param		listener - Listener pointing to a function or member-method
		\return		bool - True on successfully removal; False if could not find id or listener
	*/
	bool					Remove( const EventType& id, const EventListener& listener );

	/*! \brief		Instant call of all registered listeners to this event
		\param		theEvent - Event as interface-type containing event-data
		\return		bool - True on successfully notifying all listeners
	*/
	bool					Bang( const IMessengerEventStrong& theEvent );

	/*! \brief		Queuing an event will wait to next update-iteration and send all
					enqueued events at one time
		\param		theEvent - Event as interface-type containing event-data
		\param		type - Put queued message to logic or scene node queue (different processing times)
		\return		bool - True on successfully enqueuing event
	*/
	bool					BangQueued( const IMessengerEventStrong& theEvent, MessageQueueType type = QUEUETYPE_LOGIC );
	
	/*! \brief		Queuing an event timed will wait to next update-iteration and
					check if the given delay has been exceeded, if exceeded: Bang event
		\param		theEvent - Event as interface-type containing event-data
		\param		delay - Time that will pass before banging this event
		\param		type - Put queued message to logic or scene node queue (different processing times)
		\return		bool - True on successfully enqueuing event
	*/
	bool					BangTimed( const IMessengerEventStrong& theEvent, const float delay, MessageQueueType type = QUEUETYPE_LOGIC );

	/*! \brief		Will remove a given event from event-queue
		\param		id - ID fetched by IMessengerEvent::GetEventID()
		\param		allOfType - Determines if only first result will be removed or all of the same type
		\param		type - Search in Logic or SceneNode queue
		\return		bool - True if event could be found and removed
	*/
	bool					AbortEvent( const EventType& id, const bool allOfType, MessageQueueType type = QUEUETYPE_LOGIC );

private:

	/*! \brief		Should be called at START of game logic update iteration
		\param		maxMil - Time in milliseconds this loop is allowed to consume
	*/
	void					ProcessQueuedEvents( unsigned int maxMil = LE_INFINITE );

	void					ProcessQueuedEventsSceneNode( unsigned int maxMil = LE_INFINITE );
	
	void					ProcessEvents( uint_32 maxMil, unsigned char& usedEventQueue, uint_32& lastTime, MessageQueueType type );

	/*! \brief		Returns the index of the queue currently used depending on queue type
		\return		Boon::uint_32 - Index of the queue
	*/
	uint_32					GetQueueIndex( MessageQueueType type );

	virtual void			OnInitialize();
	virtual void			OnDestroy();


	MapListenersPerEvent				m_Listeners;			/*!< Storing all registered listeners per event-id */
	ListQueuedEvents					m_EventQueue[4];		/*!< Storing all queued events */
	unsigned char						m_UsedEventQueue;		/*!< Storing index of queue currently used */
	unsigned char						m_UsedEventQueueSN;		/*!< Storing index of queue currently used for SceneNode-Events */
	
	// allowing process manager to only call Update()
	friend class						ProcessManager;
};


}